package com.zyncro.remoting.rest;

import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.remoting.RemoteInvocationFailureException;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.LongSerializationPolicy;
import com.google.gson.stream.JsonReader;
import com.zyncro.api.model.ErrorServiceApi;
import com.zyncro.remoting.rest.client.IRestErrorHandler;
import com.zyncro.remoting.rest.client.IRestResponse;

public class ZyncroRestErrorHandler implements IRestErrorHandler {
	private static final long serialVersionUID = -7148114345303061884L;

	private final Log logger = LogFactory.getLog(getClass());

	@Override
	public Throwable handle(IRestResponse response) {
		String cl = response.getHeader(HttpHeaders.CONTENT_LENGTH);
		long contentLenght = Long.parseLong(cl);
		String contentType = response.getHeader(HttpHeaders.CONTENT_TYPE);
		if (contentLenght > 0) {
			if (MediaType.APPLICATION_JSON.equals(contentType)) {
				GsonBuilder gs = new GsonBuilder();
				gs.serializeNulls();
				gs.disableHtmlEscaping();
				gs.setLongSerializationPolicy(LongSerializationPolicy.STRING);
				Gson gson = gs.create();
				InputStreamReader isr = new InputStreamReader(response.getStream());
				JsonReader jsonReader = new JsonReader(isr);
				ErrorServiceApi error = gson.fromJson(jsonReader, ErrorServiceApi.class);
				if (StringUtils.isNotBlank(error.getError())) {
					try {
						Class<?> clazz = Class.forName(error.getError());
						Constructor<?>[] constructors = clazz.getConstructors();
						for (Constructor<?> c : constructors) {
							Class<?>[] parameterTypes = c.getParameterTypes();
							if (parameterTypes != null && parameterTypes.length == 1 && String.class.isAssignableFrom(parameterTypes[0])) {
								try {
									Object throwable = c.newInstance(error.getMessage());
									Throwable t = (Throwable) throwable;
									return t;
								} catch (InvocationTargetException ite) {
									logger.warn(String.format("Couldn't invoke constructor on %1$s(String)", error.getError()));
								} catch (IllegalAccessException iae) {
									logger.warn(String.format("Couldn't invoke constructor on %1$s(String)", error.getError()));
								} catch (InstantiationException ie) {
									logger.warn(String.format("Couldn't invoke constructor on %1$s(String)", error.getError()));
								} catch (ClassCastException cce) {
									logger.warn(String.format("Class %1$s is not throwable class", error.getError()));
								}
							}
						}
					} catch (ClassNotFoundException cnfe) {
						logger.warn(String.format("Couldn't find class %1$s", error.getError()));
					}
					return new RuntimeException(error.getError() + ":" + error.getMessage());
				}
				return new RuntimeException(error.getMessage());
			}
			return new RemoteInvocationFailureException("Content type not supported" + contentType, (Throwable) null);
		}
		return new RuntimeException(response.getBody());
	}

}
